﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RainEngine.SceneNodes;

namespace RainEngine
{
    /// <summary>
    /// 表示对象在世界空间中的位置。
    /// 进行BB和AABB碰撞检测
    /// </summary>
    public class WorldPose
    {
        /// <summary>
        /// 创建一个新WorldPose对象。
        /// </summary>
        public WorldPose()
        {
            Rotation = Quaternion.Identity;
            Position = Vector3.Zero;
            Scale = Vector3.One;
            UpdateMatrix();

            aaBB = new BoundingBox();
            aaBBCorners = new Vector3[8];
        }

        /// <summary>
        /// 缩放。
        /// 注意：这个公有成员不能被改变，需要使用SetScale()方法代替。
        /// </summary>
        public Vector3 Scale;
        
        /// <summary>
        /// 位置。
        /// 注意：这个公有成员不能被改变，需要使用 SetPosition()方法代替。
        /// </summary>
        public Vector3 Position;
        
        /// <summary>
        /// 旋转。
        /// 注意：这个公有成员不能被改变，需要使用SetRotation()方法代替。
        /// </summary>
        public Quaternion Rotation;
        
        /// <summary>
        /// 世界变换矩阵。
        /// 注意：这个公有成员不能被改变，它是基于SROT因数计算出来的。
        /// </summary>
        public Matrix WorldMatrix;
        
        /// <summary>
        /// 所属SceneNode。
        /// </summary>
        public SceneNode Node;

        internal Matrix ScaleMatrix;
        internal Matrix RotationMatrix;
        internal Matrix TranslateMatrix;

        #region AABB和半径

        private float radius;
        private Vector3 geometryCenter;
        private Vector3 geometryOffset;
        private Vector3[] aaBBCorners;
        private float localSize = 1f;
        private float maxSize = 1;

        /// <summary>
        /// 轴对齐包围盒。
        /// </summary>
        internal BoundingBox aaBB;

        /// <summary>
        /// 获取世界空间中的轴对齐包围盒（AABB）。
        /// </summary>
        public BoundingBox AaBB
        {
            get { return aaBB; }
        }

        /// <summary>
        /// 返回从节点中心到AABB平面的距离。
        /// </summary>
        public float Radius
        {
            get { return radius; }
        }

        /// <summary>
        /// 获取或设置几何体中心相对于节点位置的偏移量。
        /// 偏移量的单位使用的对象空间中的单位。除了地形，引擎中的所有SceneNodes偏移量为0，即节点的位置就是它的中心。
        /// </summary>
        public Vector3 GeometryOffset
        {
            get { return geometryOffset; }
            set 
            { 
                geometryOffset = value; 
                UpdateBB(); 
            }
        }

        /// <summary>
        /// 返回节点的几何中心。
        /// </summary>
        public Vector3 GeometryCenter
        {
            get { return geometryCenter; }
        }
        
        /// <summary>
        /// 获取或设置本地大小用来计算包围盒。local size就是对象空间中的最大包围球大小。
        /// LocalSize通常为1.0。除了地形，引擎中的所有SceneNodes的LocalSize = 1，因为它们的体积在最长轴上是-0.5到+0.5。 
        /// </summary>
        public float LocalSize
        {
            get { return localSize; }
            set 
            {
                localSize = value;
                UpdateMaxSize(); 
                UpdateBB(); 
            }
        }        

        /// <summary>
        /// 返回轴对齐包围盒（AABB）。
        /// </summary>
        /// <param name="bb"></param>
        public void GetAaBB(out BoundingBox bb)
        {
            bb = this.aaBB;
        }

        /// <summary>
        /// 返回轴对齐包围盒的顶角。
        /// </summary>
        public Vector3[] AaBBCorners
        {
            get
            {
                aaBB.GetCorners(aaBBCorners);
                return aaBBCorners;
            }
        }
        private void UpdateMaxSize()
        {
            maxSize = MathHelper.Max(MathHelper.Max(Scale.X, Scale.Y), Scale.Z) * localSize;
        }

        private void UpdateBB()
        {
            //  we need to adjust the BB size for rotations, maximum size is achieved (for cubes) when the node is rotated 45 degrees arround one axis.
            //  最远的点的长度由勾股定理获取:sqrt(a*a + b*b)
            //  因为包围盒a = b，所以只需计算sqrt( 2* (a*a) )
            //  即等于srt(2) * a，半径为一半大小，所以可以写成sqrt(2)/2 * a
            //  sqrt(2)/2由常量SQRT2预先计算
            radius = SQRT2 * maxSize;

            //  计算几何体中心
            geometryCenter = Position + (geometryOffset * maxSize / localSize);

            //  计算最小和最大顶点
            Vector3 halfLength = new Vector3(radius, radius, radius);
            aaBB.Max = halfLength + geometryCenter;
            aaBB.Min = -halfLength + geometryCenter;
        }

        /// <summary>
        /// 预计算srt(2)/2
        /// </summary>
        private const float SQRT2 = 1.4142135623730950488016887242097f / 2f;
        #endregion

        /// <summary>
        /// 设置新位置。
        /// </summary>
        /// <param name="position"></param>
        public void SetPosition(ref Vector3 position)
        {
            Position = position;
            TranslateMatrix = Matrix.CreateTranslation(Position);
            RebuildWorldMatrix();
            UpdateBB();
            if (Node != null)
            {
                Node.OnPositionChange();
            }
        }

        /// <summary>
        /// 设置新旋转。
        /// </summary>
        /// <param name="rot"></param>
        public void SetRotation(ref Quaternion rot)
        {
            Rotation = rot;
            RotationMatrix = Matrix.CreateFromQuaternion(Rotation);
            /*if (Node != null)
            {
                Vector3 eulerAngles = Utility.QuaternionToEulerAngles(ref rot);
                Node.AngleRotation = eulerAngles;
            }*/
            RebuildWorldMatrix();
        }

        /// <summary>
        /// 设置新缩放。
        /// </summary>
        /// <param name="scale"></param>
        public void SetScale(ref Vector3 scale)
        {
            Scale = scale;
            ScaleMatrix = Matrix.CreateScale(Scale);
            RebuildWorldMatrix();
            UpdateMaxSize();
            UpdateBB();

            if (Node != null)
            {
                Node.OnScaleChange();
            }
        }

        /// <summary>
        /// 计算世界矩阵(SROT)和内部AABB data.
        /// </summary>
        public void UpdateAll()
        {
            UpdateMatrix();
            UpdateMaxSize();
            UpdateBB();
        }

        /// <summary>
        /// 计算世界矩阵(SROT)。
        /// </summary>
        public void UpdateMatrix()
        {
            ScaleMatrix = Matrix.CreateScale(Scale);
            RotationMatrix = Matrix.CreateFromQuaternion(Rotation);
            TranslateMatrix = Matrix.CreateTranslation(Position);
            RebuildWorldMatrix();
        }

        private void RebuildWorldMatrix()
        {
            Matrix.Multiply(ref ScaleMatrix, ref RotationMatrix, out WorldMatrix);
            WorldMatrix.Translation = Position;
        }
        
        /// <summary>
        /// 返回一个字符串，包含位置，缩放和旋转。        
        /// </summary>
        /// <returns></returns>
        public string DebugString()
        {
            return "Loc:" + this.Position.ToString() + ", Sca:" + this.Scale.ToString() + ", Rot:" + this.Rotation.ToString();
        }

        /// <summary>
        /// 将当前实例复制到给定的WorldPose中。
        /// </summary>
        /// <param name="wp"></param>
        public void CopyTo(WorldPose wp)
        {
            wp.Scale = Scale;
            wp.Position = Position;
            wp.Rotation = Rotation;
            wp.Node = Node;
            wp.aaBB = aaBB;
            wp.geometryOffset = geometryOffset;
            wp.localSize = localSize;
            wp.UpdateAll();
        }
    }
}